import java.util.HashMap;

/**
 * Authors - Oren Bracha, Rony Lahav & Yehuda Eterman
 * This class handles all incoming messages by checking their validity and parsing their content,
 * and also creates the outgoing messages according to the message type and the received parameters
 */

public class MessageCenter {

	
	private static MessageCenter messageCenter = null;
	private static Logger logger=null;

	private String from;
	private String ipFrom;
	private String portFrom;
	private String to;
	private String ipTo;
	private String portTo;
	private String conType;
	
	public static String newline = System.getProperty("line.separator");
	
	/*Global parameters of the different variables and message types.
	 * used in this class and in other classes as well*/
	
	public final static String TYPE_UPDATE = "update";
	public final static String TYPE_REQUESTENTRY = "requestentry";
	public final static String TYPE_ENTRY = "entry";
	public final static String TYPE_REQUESTPICTURE = "requestpicture";
	public final static String TYPE_PICTURE = "picture";
	public final static String TYPE_MESSAGE = "messagetype";
	public final static String TYPE_VAR = "vartype";
	
	public final static String VAR_DBVERSION = "dbversion";
	public final static String VAR_PICVERSION = "picversion";
	public final static String VAR_PEERVERSION = "peerversion";
	public final static String VAR_PEERNAME = "peername";
	public final static String VAR_LIST = "list";
	public final static String VAR_RANGE = "range";
	public final static String VAR_RANGEFIRST = "rangefirst";
	public final static String VAR_RANGELAST = "rangelast";
	public final static String VAR_RCVWIN = "rcvwin";
	public final static String VAR_IP = "ip";
	public final static String VAR_TCP = "tcp";
	public final static String VAR_UDP = "udp";
	public final static String VAR_PHONE = "phone";
	public final static String VAR_EMAIL = "email";
	public final static String VAR_LASTCONTIME = "lastconnectiontime";
	public final static String VAR_STATUS = "status";
	public final static String VAR_PICSIZE = "picsize";
	public final static String VAR_DATA = "data";
	public final static String VAR_SN = "sn";
	public final static String VAR_N = "n";
	
	
	//Singleton CTOR
	private MessageCenter() {
		logger = Logger.getInstance();
		this.from = null;
		this.ipFrom = null;
		this.portFrom = null;
		this.to = null;
		this.portTo = null;
		this.ipTo = null;
	}
	
	//gets an in-message, checks for valid format and content and parses the parameters
	public HashMap<String,String> parseValidMessage(String message, String from, String ipFrom, String portFrom, String to, String ipTo, String portTo, boolean isTcp) {
		if (message == null)
			return null;
		message = message.toLowerCase();
		//cut the end line at the end
		if (message.endsWith(newline))
			message=message.substring(0, message.length()-1);
		//update TO and FROM data
		this.from = from;
		this.ipFrom = ipFrom;
		this.portFrom = portFrom;
		this.to = to;
		this.portTo = portTo;
		this.ipTo = ipTo;
		int firstIndex = message.indexOf("(");
		int secondIndex = message.indexOf("<");
		//update connection type
		if (isTcp)
			conType = "tcp";
		else
			conType = "udp";
		
		if (firstIndex != -1 || secondIndex != -1)
		{
			String messageType;
			int minIndex;
			if (firstIndex < secondIndex && firstIndex != -1) {
				minIndex = firstIndex;
			}
			else
			{
				minIndex = secondIndex;
			}
			
			messageType = message.substring(0, minIndex); 
			//if one of the checks throws an exception because of any unexpected reason we return an unknown error
			try {
				if (messageType.compareTo(TYPE_UPDATE) == 0)
				{
					return parseValidMessage_Update(message.substring(minIndex+1));
				}
				else if (messageType.compareTo(TYPE_REQUESTENTRY) == 0)
				{
					return parseValidMessage_RequestEntry(message.substring(minIndex+1));
				}
				else if (messageType.compareTo(TYPE_ENTRY) == 0)
				{
					return parseValidMessage_Entry(message.substring(minIndex+1));
				}
				if (messageType.compareTo(TYPE_REQUESTPICTURE) == 0)
				{
					return parseValidMessage_RequestPicture(message.substring(minIndex+1));
				}
				else if (messageType.compareTo(TYPE_PICTURE) == 0)
				{
					return parseValidMessage_Picture(message.substring(minIndex+1));
				}
			}
			catch(Exception e){
				return logger.printErrorWrongFormat("unknown", message, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			}
		}
		return logger.printErrorWrongFormat("unknown", message, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
	}
	
	//checks UPDATE message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_Update(String msg) {
		String originalMsg = msg;
		//for the first time we get an UPDATE message we still don't know where it came from
		if (this.from == null)
			this.from = msg.split(VAR_PEERNAME + "=")[1].split(">")[0];
		if (!msg.endsWith(">"))
			return logger.printErrorWrongFormat(TYPE_UPDATE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		msg = msg.substring(0, msg.length()-1);
		//split the parameters
		String[] vars = msg.split(",\\$");
		//exactly 2 parameters allowed
		if (vars.length != 2)
			return logger.printErrorWrongFormat(TYPE_UPDATE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		if (!vars[0].startsWith("$" + VAR_DBVERSION) || !vars[1].startsWith(VAR_PEERNAME))
			return logger.printErrorWrongFormat(TYPE_UPDATE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		HashMap<String,String> params = new HashMap<String,String>();
		//each parameter has the name and value only
		String[] dbv = vars[0].split("=");
		//version format validity check
		if (dbv.length != 2 || !isVersionFormatValid(dbv[1]))
			return logger.printErrorWrongFormat(TYPE_UPDATE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_DBVERSION, dbv[1]);
		//split the variables
		String[] pn = vars[1].split("=");
		//PEERNAME validity
		if (pn.length != 2 || !isPeerNameValid(pn[1]))
			return logger.printErrorWrongFormat(TYPE_UPDATE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_PEERNAME, pn[1]);
		params.put(TYPE_MESSAGE, TYPE_UPDATE);
		return params;
	}
	
	//checks REQUESTENTRY message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_RequestEntry(String msg) {
		String originalMsg = msg;
		if (!msg.endsWith(">"))
			return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		msg = msg.substring(0, msg.length()-1);
		//split the parameters
		String[] vars = msg.split(",\\$");
		//maximum 2 parameters
		if (vars.length > 2)
			return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		HashMap<String,String> params = new HashMap<String,String>();
		//if there's only one parameter
		if (vars.length == 1){
			//it has to be dbversion
			if (!vars[0].startsWith("$" + VAR_DBVERSION))
				return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			//split the variables
			String[] dbv = vars[0].split("=");
			//check valid date-time format
			if (dbv.length != 2 || !isVersionFormatValid(dbv[1]))
				//if it's not a full date-time it can be version "0"
				if (!dbv[1].equals("0"))
					return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			if (dbv[1].equals("0"))
				dbv[1] = DateStringConverter.getZeroDateAsString();
			params.put(VAR_DBVERSION, dbv[1]);
		}
		//there are 2 parameters - dbversion and a list or range of peers
		else{
			//check dbversion as above
			if (!vars[0].startsWith("$" + VAR_DBVERSION) || !(vars[1].startsWith(VAR_LIST) || vars[1].startsWith(VAR_RANGE)))
				return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			//list or range in inside clauses
			if (!vars[1].endsWith(")"))
				return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			String[] dbv = vars[0].split("=");
			if (dbv.length != 2 || !isVersionFormatValid(dbv[1]))
				if (!dbv[1].equals("0"))
					return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			if (dbv[1].equals("0"))
				dbv[1] = DateStringConverter.getZeroDateAsString();
			params.put(VAR_DBVERSION, dbv[1]);
			//if it has a list
			if (vars[1].startsWith(VAR_LIST + "(")){
				//isolate variables
				vars[1] = vars[1].substring(0, vars[1].length()-1).substring(5);
				//split variables
				String[] list = vars[1].split(",");
				//less than one variable is illegal
				if (list.length < 1)
					return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
				params.put(TYPE_VAR, VAR_LIST);
				params.put(VAR_LIST,vars[1]);
			}
			else
				//if it has a range
				if (vars[1].startsWith(VAR_RANGE + "(")){
					//isolate variables
					vars[1] = vars[1].substring(0, vars[1].length()-1).substring(6);
					//split variables by '-'
					if (vars[1].contains("-")){
						String[] range = vars[1].split("-");
						//exactly 2 variables
						if (range.length != 2)
							return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
						params.put(TYPE_VAR, VAR_RANGE);
						params.put(VAR_RANGEFIRST, range[0]);
						params.put(VAR_RANGELAST, range[1]);
					}
					else{
						//split variables by ','
						String[] range = vars[1].split(",");
						//exactly 2 variables
						if (range.length != 2)
							return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
						params.put(TYPE_VAR, VAR_RANGE);
						params.put(VAR_RANGEFIRST, range[0]);
						params.put(VAR_RANGELAST, range[1]);
					}
				}
				else
					return logger.printErrorWrongFormat(TYPE_REQUESTENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		}
		params.put(TYPE_MESSAGE, TYPE_REQUESTENTRY);
		return params;
	}
	
	//checks ENTRY message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_Entry(String msg) {
		String originalMsg = msg;
		if (!msg.endsWith(">"))
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		//isolate parameters
		msg = msg.substring(0, msg.length()-1);
		String[] splittedMsg = msg.split("\\)<");
		if (splittedMsg.length != 2)
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		HashMap<String,String> params = new HashMap<String,String>();
		//get serial number of message "SN" and total "N" messages
		try{
			String[] nums = splittedMsg[0].split(",\\$");
			//exactly 2 parameters
			if (nums.length != 2)
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			//first is the SN number and then the N number
			if (!nums[0].startsWith("$" + VAR_SN) || !nums[1].startsWith(VAR_N))
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			//split variables
			String[] splittedNums = nums[0].split("=");
			//exactly 2 variables
			if (splittedNums.length != 2)
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			int sn = Integer.parseInt(splittedNums[1]);
			//SN can't be 0 or lower
			if (sn <= 0)
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_SN,splittedNums[1]);
			//split variables
			splittedNums = nums[1].split("=");
			//exactly 2 variables
			if (splittedNums.length != 2)
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			int n = Integer.parseInt(splittedNums[1]);
			//N has to be bigger or equal to SN (that is bigger than o)
			if (n < sn)
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_N,splittedNums[1]);
		}
		catch (Exception e){
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		}
		//get all variables of all parameters
		String[] vars=splittedMsg[1].split(",\\$");
		//exactly 12 parameters
		if (vars.length != 12)
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		//in the exact order of content
		if (!(vars[0].startsWith("$" + VAR_DBVERSION) && vars[1].startsWith(VAR_PEERVERSION) && vars[2].startsWith(VAR_PEERNAME) && vars[3].startsWith(VAR_IP) && 
				vars[4].startsWith(VAR_TCP) && vars[5].startsWith(VAR_UDP) && vars[6].startsWith(VAR_PHONE) && vars[7].startsWith(VAR_EMAIL) && 
				vars[8].startsWith(VAR_LASTCONTIME) && vars[9].startsWith(VAR_STATUS) && vars[10].startsWith(VAR_PICSIZE) && vars[11].startsWith(VAR_PICVERSION)))
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		//get dbversion - can't be "0" here
		String[] var = vars[0].split("=");
		if (var.length != 2 || !isVersionFormatValid(var[1]))
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_DBVERSION, var[1]);
		//get peerversion
		var = vars[1].split("=");
		//exactly 2 parameters and date-time format valid
		if (var.length != 2 || !isVersionFormatValid(var[1]))
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_PEERVERSION, var[1]);
		//get peername
		var = vars[2].split("=");
		//exactly 2 parameters and name valid
		if (var.length != 2 || !isPeerNameValid(var[1]))
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_PEERNAME, var[1]);
		//get IP
		var = vars[3].split("=");
		//exactly 2 parameters and IP consists of 4 sections separated by '.' 
		if (var.length != 2 || var[1].split("\\.").length != 4)
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_IP, var[1]);
		//get TCP and UDP
		try{
			var = vars[4].split("=");
			//exactly 2 parameters and TCP range between 1-65536
			if (var.length != 2 || !(Integer.parseInt(var[1])>0 && Integer.parseInt(var[1])<65536))
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_TCP, var[1]);
			var = vars[5].split("=");
			//exactly 2 parameters and UDP range between 1-65536
			if (var.length != 2 || !(Integer.parseInt(var[1])>0 && Integer.parseInt(var[1])<65536))
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_UDP, var[1]);
		}
		catch (Exception e){
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		}
		//get phone number
		var = vars[6].split("=");
		//exactly 2 parameters
		if (var.length != 2)
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_PHONE, var[1]);
		//get email
		var = vars[7].split("=");
		//exactly 2 parameters and contains "@" and at least one '.'
		if (var.length != 2 || !var[1].contains("@") || !var[1].contains("."))
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_EMAIL, var[1]);
		//get last connection time
		var = vars[8].split("=");
		//exactly 2 parameters and date-time format valid
		if (var.length != 2 || !isVersionFormatValid(var[1]))
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_LASTCONTIME, var[1]);
		//get STATUS and PICSIZE
		try{
			var = vars[9].split("=");
			//exactly 2 parameters and status bigger than or equal to 0
			if (var.length != 2 || !(Integer.parseInt(var[1])>=0))
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_STATUS, var[1]);
			var = vars[10].split("=");
			//exactly 2 parameters and size has to be bigger than 0
			if (var.length != 2 || !(Integer.parseInt(var[1])>0))
				return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_PICSIZE, var[1]);
		}
		catch (Exception e){
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		}
		//get picversion
		var = vars[11].split("=");
		//exactly 2 parameters and date-time format valid
		if (var.length != 2 || !isVersionFormatValid(var[1]))
			return logger.printErrorWrongFormat(TYPE_ENTRY, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_PICVERSION, var[1]);
		params.put(TYPE_MESSAGE, TYPE_ENTRY);
		return params;
	}
	
	//checks REQUESTPICTURE message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_RequestPicture(String msg) {
		String originalMsg = msg;
		if (!msg.endsWith(">"))
			return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		msg = msg.substring(0, msg.length()-1);
		//isolate parameters
		String[] vars = msg.split(",\\$");
		//between 2-4 parameters
		if (vars.length > 4 || vars.length < 2)
			return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		HashMap<String,String> params = new HashMap<String,String>();
		//has to start with dbversion
		if (!vars[0].startsWith("$" + VAR_DBVERSION))
			return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		//split variables
		String[] dbv = vars[0].split("=");
		//exactly 2 variables and date-time format valid
		if (dbv.length != 2 || !isVersionFormatValid(dbv[1]))
			return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_DBVERSION, dbv[1]);
		//has to continue with RCVWIN
		if (!vars[1].startsWith(VAR_RCVWIN))
			return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		try{
			String[] rcvw = vars[1].split("=");
			//exactly 2 variables and has to be bigger than 0
			if (rcvw.length != 2 || !(Integer.parseInt(rcvw[1])>0))
				return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_RCVWIN, rcvw[1]);
		}
		catch (Exception e){
			return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		}
		//if these are the only parameters we finished parsing
		if (vars.length == 2){
			params.put(TYPE_MESSAGE, TYPE_REQUESTPICTURE);
			return params;
		}
		else{
			//if we have a specific peer to handle - get the PEERNAME
			if (!vars[2].startsWith(VAR_PEERNAME))
				return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			String[] pn = vars[2].split("=");
			//exactly 2 variables and name valid
			if (pn.length != 2 || !isPeerNameValid(pn[1]))
				return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_PEERNAME, pn[1]);
			if (vars.length == 3){
				params.put(TYPE_MESSAGE, TYPE_REQUESTPICTURE);
				return params;
			}
			else{
				//if we have a list
				if (vars[3].startsWith(VAR_LIST + "(")){
					//isolate variables
					vars[3] = vars[3].substring(0, vars[3].length()-1).substring(5);
					String[] list = vars[3].split(",");
					//has to have at least one peer in the list
					if (list.length < 1)
						return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
					params.put(TYPE_VAR, VAR_LIST);
					params.put(VAR_LIST,vars[3]);
				}
				else
					//if we have a range
					if (vars[3].startsWith(VAR_RANGE + "(")){
						vars[3] = vars[3].substring(0, vars[3].length()-1).substring(6);
						//split variables by '-'
						if (vars[3].contains("-")){
							String[] range = vars[3].split("-");
							//exactly 2 variables
							if (range.length != 2)
								return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
							params.put(TYPE_VAR, VAR_RANGE);
							params.put(VAR_RANGEFIRST, range[0]);
							params.put(VAR_RANGELAST, range[1]);
						}
						else{
							//split variables by ','
							String[] range = vars[3].split(",");
							//exactly 2 variables
							if (range.length != 2)
								return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
							params.put(TYPE_VAR, VAR_RANGE);
							params.put(VAR_RANGEFIRST, range[0]);
							params.put(VAR_RANGELAST, range[1]);
						}
					}
					else
						return logger.printErrorWrongFormat(TYPE_REQUESTPICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			}
		}
		params.put(TYPE_MESSAGE, TYPE_REQUESTPICTURE);
		return params;
	}
	
	//checks REQUESTPICTURE message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_Picture(String msg) {
		String originalMsg = msg;
		if (!msg.endsWith(">"))
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		msg = msg.substring(0, msg.length()-1);
		//separate parameters
		String[] splittedMsg = msg.split("\\)<");
		if (splittedMsg.length<2)
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		String[] nums = splittedMsg[0].split(",\\$");
		if (nums.length != 2)
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		if (!nums[0].startsWith("$" + VAR_SN) || !nums[1].startsWith(VAR_N))
			return logger.printErrorWrongFormat(TYPE_PICTURE, msg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		HashMap<String,String> params = new HashMap<String,String>();
		//get the SN and N numbers
		try{
			String[] splittedNums = nums[0].split("=");
			if (splittedNums.length != 2)
				return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			int sn = Integer.parseInt(splittedNums[1]);
			//SN has to be bigger than 0
			if (sn <= 0)
				return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_SN,splittedNums[1]);
			splittedNums = nums[1].split("=");
			if (splittedNums.length != 2)
				return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			int n = Integer.parseInt(splittedNums[1]);
			//N has to be bigger than SN (that is bigger than 0)
			if (n < sn)
				return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
			params.put(VAR_N,splittedNums[1]);
		}
		catch (Exception e){
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		}
		//split variables
		String[] vars=splittedMsg[1].split(",\\$");
		//exactly 3 variables
		if (vars.length != 3)
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		//in a specific order
		if (!(vars[0].startsWith("$" + VAR_PICVERSION) && vars[1].startsWith(VAR_PEERNAME) && vars[2].startsWith(VAR_DATA)))
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		//get PICVERSION
		String[] var = vars[0].split("=");
		//exactly 2 variables and date-time format valid
		if (var.length != 2 || !isVersionFormatValid(var[1]))
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_PICVERSION, var[1]);
		//get PEERNAME
		var = vars[1].split("=");
		//exactly 2 variables and name valid
		if (var.length != 2 || !isPeerNameValid(var[1]))
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_PEERNAME, var[1]);
		//get data
		var = vars[2].split("=");
		if (var.length != 2)
			return logger.printErrorWrongFormat(TYPE_PICTURE, originalMsg, this.from, this.ipFrom, this.portFrom, this.to, this.ipTo, this.portTo, conType);
		params.put(VAR_DATA, var[1]);
		
		params.put(TYPE_MESSAGE, TYPE_PICTURE);
		return params;
	}
	
	//singleton
	public static MessageCenter getInstance() {
		if (messageCenter == null) {
			messageCenter = new MessageCenter();
		}
		
		return messageCenter;
	}
	
	//checks for valid peer name - starts with 1 and total length of 10 
	private boolean isPeerNameValid(String name){
		if (name.startsWith("1") && name.length() == 10)
			return true;
		else
			return false;
	}
	
	//checks for valid date-time format of DD/MM/YY;HH:MM:SS
	private boolean isVersionFormatValid(String version){
		String[] dateTime = version.split(";");
		if (dateTime.length != 2)
			return false;
		String[] date = dateTime[0].split("/");
		if (date.length != 3)
			return false;
		if (date[0].length() != 2 || date[1].length() != 2 || date[2].length() != 2)
			return false;
		int day = Integer.parseInt(date[0]);
		if (!(day>0 && day<32))
			return false;
		int month = Integer.parseInt(date[1]);
		if (!(month>0 && month<13))
			return false;
		if ((month==2 && day>29) || ((month==4 || month==6 || month==9 || month==11) && day>30))
			return false;
		String[] time = dateTime[1].split(":");
		if (time.length != 3)
			return false;
		if (time[0].length() != 2 || time[1].length() != 2 || time[2].length() != 2)
			return false;
		int hour = Integer.parseInt(time[0]);
		if (!(hour>=0 && hour<25))
			return false;
		int min = Integer.parseInt(time[1]);
		if (!(min>=0 && min<61))
			return false;
		int sec = Integer.parseInt(time[2]);
		if (!(sec>=0 && sec<61))
			return false;
		return true;
	}
	
	//creates an UPDATE message
	public String createMessage_Update(String dbversion, String peername){
		return TYPE_UPDATE + "<$" + VAR_DBVERSION + "=" + dbversion + ",$" + VAR_PEERNAME + "=" + peername + ">";
	}
	
	//creates a REQUESTENTRY message with dbversion only
	public String createMessage_RequestEntry_DB(String dbversion){
		if (dbversion.equals("0"))
			dbversion = DateStringConverter.getZeroDateAsString();
		return TYPE_REQUESTENTRY + "<$" + VAR_DBVERSION + "=" + dbversion + ">";
	}
	
	//creates a REQUESTENTRY message with dbversion and a list
	public String createMessage_RequestEntry_DB_List(String dbversion, String list){
		String db = createMessage_RequestEntry_DB(dbversion);
		return db.substring(0,db.length()-1) + ",$" + VAR_LIST + "(" + list + ")>";
	}
	
	//creates a REQUESTENTRY message with dbversion and a range
	public String createMessage_RequestEntry_DB_Range(String dbversion, String rangeFirst, String rangeLast){
		String db = createMessage_RequestEntry_DB(dbversion);
		String range = db.substring(0,db.length()-1) + ",$" + VAR_RANGE + "(";
		if (rangeFirst.equals("0") || rangeLast.equals("ALL"))
			return range + rangeFirst + "," + rangeLast + ")>";
		else
			return range + rangeFirst + "-" + rangeLast + ")>";
	}
	
	//creates an ENTRY message
	public String createMessage_Entry(String sn, String n, String dbversion, String peerversion, String peername, String ip, String tcp, String udp, String phone, String email, String lastConnectionTime, String status, String picSize, String picVersion){
		return TYPE_ENTRY + "($" + VAR_SN + "=" + sn + ",$" + VAR_N + "=" + n + ")<$" + VAR_DBVERSION + "=" + dbversion + ",$" + VAR_PEERVERSION + "=" + peerversion + ",$" + VAR_PEERNAME + "=" + peername + ",$" + VAR_IP + "=" + ip + ",$" + VAR_TCP + "=" + tcp + ",$"
		+ VAR_UDP + "=" + udp + ",$" + VAR_PHONE + "=" + phone + ",$" + VAR_EMAIL + "=" + email + ",$" + VAR_LASTCONTIME + "=" + lastConnectionTime + ",$" + VAR_STATUS + "=" + status + ",$" + VAR_PICSIZE + "=" + picSize + ",$" + VAR_PICVERSION + "=" + picVersion + ">";
	}
	
	//creates a REQUESTPICTURE with dbversion and rcvwin only
	public String createMessage_RequestPicture_DB_RCV(String dbversion, String rcvWin){
		return TYPE_REQUESTPICTURE + "<$" + VAR_DBVERSION + "=" + dbversion + ",$" + VAR_RCVWIN + "=" + rcvWin + ">";
	}
	
	//creates a REQUESTPICTURE with dbversion and rcvwin and a specific peer
	public String createMessage_RequestPicture_DB_RCV_Peer(String dbversion, String rcvWin, String peerName){
		String db = createMessage_RequestPicture_DB_RCV(dbversion,rcvWin);
		return db.substring(0,db.length()-1) + ",$" + VAR_PEERNAME + "=" + peerName + ">";
	}
	
	//creates a REQUESTPICTURE with dbversion and rcvwin and a list
	public String createMessage_RequestPicture_DB_RCV_Peer_List(String dbversion, String rcvWin, String peerName, String list){
		String db = createMessage_RequestPicture_DB_RCV_Peer(dbversion, rcvWin, peerName);
		return db.substring(0,db.length()-1) + ",$" + VAR_LIST + "(" + list + ")>";
	}
	
	//creates a REQUESTPICTURE with dbversion and rcvwin and a range
	public String createMessage_RequestPicture_DB_RCV_Peer_Range(String dbversion, String rcvWin, String peerName, String rangeFirst, String rangeLast){
		String db = createMessage_RequestPicture_DB_RCV_Peer(dbversion, rcvWin, peerName);
		return db.substring(0,db.length()-1) + ",$" + VAR_RANGE + "(" + rangeFirst + "-" + rangeLast + ")>";
	}
	
	//creates a PICTURE message
	public String createMessage_Picture(String sn, String n, String picversion, String peername, String data){
		return TYPE_PICTURE + "($" + VAR_SN + "=" + sn + ",$" + VAR_N + "=" + n + ")<$" + VAR_PICVERSION + "=" + picversion + ",$" + VAR_PEERNAME + "=" + peername + ",$" + VAR_DATA + "=" + data + ">";
	}	

}
